package com.dj.ms.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.dj.common.support.HttpRequestWrapper;
import com.dj.common.support.HttpResponseWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ResponseFacade;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.MDC;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * <br>
 * <p>
 * - mvc log
 * </p>
 *
 * <br>
 *
 * @author ZJG
 * @version 1.0
 * @date 2021/12/1 上午9:42
 */
@Slf4j
@WebFilter(filterName = "webServiceLogCircular", urlPatterns = "/*")
public class WebServiceLogCircular implements Filter {

    private static final List<String> ignorePathRegex = Lists.newArrayList("/actuator");
    private static final String ignoreUrlRegex = ".*((/index)|(/index/.*)|([.]((html)|(jsp)|(css)|(js)|(gif)|(png))))$";
    private static final String charset = "UTF-8";
    private static final String requestId = "requestId";

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String simpleUUID = IdUtil.fastSimpleUUID();
        ThreadContext.put(requestId, simpleUUID);
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        String traceId = httpServletRequest.getHeader("traceId");
        if (StringUtils.isEmpty(traceId)) {
            traceId = simpleUUID;
        }
        MDC.put("traceId", traceId);

        String uri = httpServletRequest.getRequestURI();
        if (StrUtil.containsAny(uri, "upload")) {
            ThreadContext.clearAll();
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpServletRequest, ThreadContext.get(requestId));
        HttpResponseWrapper responseWrapper = new HttpResponseWrapper(ThreadContext.get(requestId), (HttpServletResponse) servletResponse);

        StringBuffer url = httpServletRequest.getRequestURL();
        String domain = url.delete(url.length() - uri.length(), url.length()).append("/").toString();

        // html页面、js不打印日志 & fegin api不打印日志
        if (uri.matches(ignoreUrlRegex)
                || CollectionUtil.contains(ignorePathRegex, regx -> StrUtil.startWithIgnoreCase(uri, regx))) {
            ThreadContext.clearAll();
            filterChain.doFilter(servletRequest, responseWrapper);
            return;
        }

        // 打印from格式的入参信息
        Map params = servletRequest.getParameterMap();
        String parameters = requestWrapper.getBody();
        if (CollectionUtil.isNotEmpty(params)
                && StrUtil.isBlank(parameters)) {
            parameters = JSON.toJSONString(params);
        }
        filterChain.doFilter(requestWrapper, responseWrapper);

        String outParam = new String();
        params = Maps.newHashMap();
        try {
            // 如果响应头存在errorCode则打印，除文件下载外均不存在
            params.put("errorCode", ((ResponseFacade) servletResponse).getHeader("errorCode"));
            params.put("errorMsg", (URLDecoder.decode(((ResponseFacade) servletResponse).getHeader("errorMsg"), charset)));
            outParam = JSON.toJSONString(params);
        } catch (Exception e) {
        }

        // 记录出参响应体
        if (params.size() < 2) {
            outParam = outParam + StrUtil.removeAllLineBreaks(responseWrapper.getBodyString());
        }

        if (log.isInfoEnabled()) {
            log.info("request domain：{}, uri：{}, parameters: {}, response body: {}", domain, uri, parameters, outParam);
        }

        ThreadContext.clearAll();
    }
}
